% Functional Reactive Programming in Oz

% Author: Peter Van Roy
% April 28, 2006

% Functional Reactive Programming is like functional
% programming except that each function can accept a
% stream of input values.  If a new input value arrives
% on an argument, then the function is reevaluated with
% all the most recent input values and a new output
% value is created.  This is a kind of dataflow
% programming.  It can handle nondeterminism, so it is
% more expressive than declarative concurrency.  In fact,
% it can be implemented with the declarative concurrent
% model extended with WaitTwo.  In addition, we have to
% avoid scheduling problems ("glitches") that happen when
% input streams are causally connected.  Glitches can
% cause program invariants to be temporarily violated, so
% they are undesirable.  For example, {Less As {Add As 1}}
% should never give 'false' as an output when elements
% arrive on As.

% The implementation given here handles the case when the
% input streams are independent.  If they have a common
% origin, then the solution has to be modified so that the
% function evaluation needs both input arguments.

% FRP is similar in expressiveness to concurrent logic
% programming.  The main difference is that FRP adds
% scheduling constraints to avoid temporarily violating
% invariants.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Example of FRP in Oz

% Assume that 'Add' creates an addition that accepts streams
% (The definition of Add is given below in this file.)

% The expression a=b+(c+1) where a, b, c are streams
declare As Bs Cs in
As={Add Bs {Add Cs 1}}
{Browse As}
{Browse Bs}
{Browse Cs}

% Let's put a few elements on the input streams
Cs=2|_
Bs=1|2|3|4|_
% Now As becomes 4|5|6|7|_

% Extending Cs will cause As to be extended
Cs=2|3|_
% Now As becomes 4|5|6|7|8|_

% Extending Bs will cause As to be extended
Bs=1|2|3|4|5|6|_
% Now As becomes 4|5|6|7|8|9|10|_

% If a stream terminates, then no more values will appear on it
Bs=1|2|3|4|5|6|nil
% The 'Add Bs' operation will now always add 6 to its input
Cs=2|3|4|5|_
% Now As becomes 4|5|6|7|8|9|10|11|12|_

% If both Cs and Bs are terminated, then so will As
Cs=2|3|4|5|nil
% Now As becomes 4|5|6|7|8|9|10|11|12|nil

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% How to define Add

% TwoArgOp is a generic function that can be used to define any
% two-argument FRP operation.  The FRP operation accepts a single
% integer or a stream of integers.  Termination of the stream is
% handled correctly: when a stream terminates with nil then there
% are no more changes on that stream.

% Waits until at least one argument is bound
% Can return 1 if X is bound and 2 if Y is bound
declare
fun {WaitTwo X Y}
  {Record.waitOr X#Y}
end

declare
fun {TwoArgOp F}
  fun {$ As Bs}
     thread
        fun {OpLoop Aval Bval As Bs}
           case {WaitTwo As Bs}
           of 1 then
              case As of A|Ar then {F A Bval}|{OpLoop A Bval Ar Bs}
              [] nil then {Map Bs fun {$ B} {F Aval B} end}
              end
           [] 2 then
              case Bs of B|Br then {F Aval B}|{OpLoop Aval B As Br}
              [] nil then {Map As fun {$ A} {F A Bval} end}
              end
           end
        end
        fun {FirstValue As}
           case As of A|Ar then A#Ar else if {IsInt As} then As#nil end end
        end
        % Wait until each stream has its first value
        Aval#Anext={FirstValue As}
        Bval#Bnext={FirstValue Bs}
     in
        % Execution loop
        {F Aval Bval}|{OpLoop Aval Bval Anext Bnext}
     end
  end
end

declare
Add={TwoArgOp fun {$ A B} A+B end}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
